#include "../Server_version3.hpp"
#include <fstream>
#include <sstream>
#include <ctype.h>
#include <sys/stat.h>
#include <regex>
/// 代码注重美观的话，就是越节俭越美观，类似张文超那个确实就是比较美观的代码了

#define indexurl std::string("wwwroot")
#define indexhtml "index.html"
#define DEFAULT_TIMEOUT 10

namespace HTTP
{
    std::unordered_map<int,std::string> StatusDesc
    {{100,  "Continue"},
    {101,  "Switching Protocol"},
    {102,  "Processing"},
    {103,  "Early Hints"},
    {200,  "OK"},
    {201,  "Created"},
    {202,  "Accepted"},
    {203,  "Non-Authoritative Information"},
    {204,  "No Content"},
    {205,  "Reset Content"},
    {206,  "Partial Content"},
    {207,  "Multi-Status"},
    {208,  "Already Reported"},
    {226,  "IM Used"},
    {300,  "Multiple Choice"},
    {301,  "Moved Permanently"},
    {302,  "Found"},
    {303,  "See Other"},
    {304,  "Not Modified"},
    {305,  "Use Proxy"},
    {306,  "unused"},
    {307,  "Temporary Redirect"},
    {308,  "Permanent Redirect"},
    {400,  "Bad Request"},
    {401,  "Unauthorized"},
    {402,  "Payment Required"},
    {403,  "Forbidden"},
    {404,  "Not Found"},
    {405,  "Method Not Allowed"},
    {406,  "Not Acceptable"},
    {407,  "Proxy Authentication Required"},
    {408,  "Request Timeout"},
    {409,  "Conflict"},
    {410,  "Gone"},
    {411,  "Length Required"},
    {412,  "Precondition Failed"},
    {413,  "Payload Too Large"},
    {414,  "URI Too Long"},
    {415,  "Unsupported Media Type"},
    {416,  "Range Not Satisfiable"},
    {417,  "Expectation Failed"},
    {418,  "I'm a teapot"},
    {421,  "Misdirected Request"},
    {422,  "Unprocessable Entity"},
    {423,  "Locked"},
    {424,  "Failed Dependency"},
    {425,  "Too Early"},
    {426,  "Upgrade Required"},
    {428,  "Precondition Required"},
    {429,  "Too Many Requests"},
    {431,  "Request Header Fields Too Large"},
    {451,  "Unavailable For Legal Reasons"},
    {501,  "Not Implemented"},
    {502,  "Bad Gateway"},
    {503,  "Service Unavailable"},
    {504,  "Gateway Timeout"},
    {505,  "HTTP Version Not Supported"},
    {506,  "Variant Also Negotiates"},
    {507,  "Insufficient Storage"},
    {508,  "Loop Detected"},
    {510,  "Not Extended"},
    {511,  "Network Authentication Required"}};

    std::unordered_map<std::string,std::string> _mime_msg{{".aac",    "audio/aac"},
    {".abw",        "application/x-abiword"},
    {".arc",        "application/x-freearc"},
    {".avi",        "video/x-msvideo"},
    {".azw",        "application/vnd.amazon.ebook"},
    {".bin",        "application/octet-stream"},
    {".bmp",        "image/bmp"},
    {".bz",         "application/x-bzip"},
    {".bz2",        "application/x-bzip2"},
    {".csh",        "application/x-csh"},
    {".css",        "text/css"},
    {".csv",        "text/csv"},
    {".doc",        "application/msword"},
    {".docx",       "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
    {".eot",        "application/vnd.ms-fontobject"},
    {".epub",       "application/epub+zip"},
    {".gif",        "image/gif"},
    {".htm",        "text/html"},
    {".html",       "text/html"},
    {".ico",        "image/vnd.microsoft.icon"},
    {".ics",        "text/calendar"},
    {".jar",        "application/java-archive"},
    {".jpeg",       "image/jpeg"},
    {".jpg",        "image/jpeg"},
    {".js",         "text/javascript"},
    {".json",       "application/json"},
    {".jsonld",     "application/ld+json"},
    {".mid",        "audio/midi"},
    {".midi",       "audio/x-midi"},
    {".mjs",        "text/javascript"},
    {".mp3",        "audio/mpeg"},
    {".mpeg",       "video/mpeg"},
    {".mpkg",       "application/vnd.apple.installer+xml"},
    {".odp",        "application/vnd.oasis.opendocument.presentation"},
    {".ods",        "application/vnd.oasis.opendocument.spreadsheet"},
    {".odt",        "application/vnd.oasis.opendocument.text"},
    {".oga",        "audio/ogg"},
    {".ogv",        "video/ogg"},
    {".ogx",        "application/ogg"},
    {".otf",        "font/otf"},
    {".png",        "image/png"},
    {".pdf",        "application/pdf"},
    {".ppt",        "application/vnd.ms-powerpoint"},
    {".pptx",       "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
    {".rar",        "application/x-rar-compressed"},
    {".rtf",        "application/rtf"},
    {".sh",         "application/x-sh"},
    {".svg",        "image/svg+xml"},
    {".swf",        "application/x-shockwave-flash"},
    {".tar",        "application/x-tar"},
    {".tif",        "image/tiff"},
    {".tiff",       "image/tiff"},
    {".ttf",        "font/ttf"},
    {".txt",        "text/plain"},
    {".vsd",        "application/vnd.visio"},
    {".wav",        "audio/wav"},
    {".weba",       "audio/webm"},
    {".webm",       "video/webm"},
    {".webp",       "image/webp"},
    {".woff",       "font/woff"},
    {".woff2",      "font/woff2"},
    {".xhtml",      "application/xhtml+xml"},
    {".xls",        "application/vnd.ms-excel"},
    {".xlsx",       "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
    {".xml",        "application/xml"},
    {".xul",        "application/vnd.mozilla.xul+xml"},
    {".zip",        "application/zip"},
    {".3gp",        "video/3gpp"},
    {".3g2",        "video/3gpp2"},
    {".7z",         "application/x-7z-compressed"}};
    
    /// @brief 这个模块是提供各种操作，方便后续模块的编写
    class Util
    {
    public:
        Util(){}
        // 字符串分割函数 std::string ss = "abc,,def,ff,";
        ///"username=123456&passwd=123444";
        static void Split(const std::string& str,const std::string& sep,std::vector<std::string>& split_result)
        {
            int startpos = 0;
            while(startpos < str.size())
            {
                auto pos = str.find(sep,startpos);
                // 此时没有找到
                if(pos == std::string::npos)
                {
                    std::string s = str.substr(startpos);
                    split_result.push_back(s);
                    return;
                }
                // 此时找到了
                std::string s = str.substr(startpos,pos-startpos);
                split_result.push_back(s);
                while(pos < str.size()-1)
                {
                    if(str[pos] == str[pos+1]){pos++;}
                    else break;
                }
                startpos = ++pos;
            }
        }
        static std::string IntToDesc(int status)
        {
            auto it = StatusDesc.find(status);
            if(it == StatusDesc.end()) return StatusDesc[400];
            return it->second;
        }
        static std::string ExtMime(const std::string &filename) 
        {    
            // a.b.txt  先获取文件扩展名
            size_t pos = filename.find_last_of('.');
            if (pos == std::string::npos) {return "application/octet-stream";}
            //根据扩展名，获取mime
            std::string ext = filename.substr(pos);
            auto it = _mime_msg.find(ext);
            if (it == _mime_msg.end()) {return "application/octet-stream";}
            return it->second;
        }
        static bool ObtainTheFileContent(std::string path,std::string* buf)
        {
            /// 不去做任何处理，直接读取二进制数据
            std::ifstream is(path,std::ios::binary);
            if(is.is_open() == false)
            {
                printf("OPEN %s FILE FAILED!!", path.c_str());
                return false;
            }
            is.seekg (0, is.end);
            int length = is.tellg();
            is.seekg (0, is.beg);
            // read data as a block:
            buf->resize(length); //开辟文件大小的空间
            is.read(&(*buf)[0], length);
            is.close();
            return true;
        }
        static bool WriteToTheFile(std::string path,const std::string& buf)
        {
            std::ofstream os(path,std::ios::binary);
            if(os.is_open() == false)
            {
                ERR_LOG("os.is_open() == false");
                return false;
            }
            os.write(buf.c_str(), buf.size());
            return true;
        }
        /// 判断一个文件是目录还是文本文件
        static bool IsDir(const std::string filename)
        {
            struct stat st;
            /// stat 失败返回-1
            if(::stat(filename.c_str(),&st) != 0)
            {
                return false;
            }
            return S_ISDIR(st.st_mode);
        }
        static bool IsRegular(const std::string filename)
        {
            struct stat st;
            /// stat 失败返回-1
            if(::stat(filename.c_str(),&st) != 0)
            {
                return false;
            }
            return S_ISREG(st.st_mode);
        }
        //http请求的资源路径有效性判断
        // /index.html  --- 前边的/叫做相对根目录  映射的是某个服务器上的子目录
        // 想表达的意思就是，客户端只能请求相对根目录中的资源，其他地方的资源都不予理会
        // /../login, 这个路径中的..会让路径的查找跑到相对根目录之外，这是不合理的，不安全的
        static bool ValidPath(const std::string &path) {
            //思想：按照/进行路径分割，根据有多少子目录，计算目录深度，有多少层，深度不能小于0
            std::vector<std::string> subdir;
            Split(path, "/", subdir);
            int level = 0;
            for (auto &dir : subdir) {
                if (dir == "..") {
                    level--; //任意一层走出相对根目录，就认为有问题
                    if (level < 0) return false;
                    continue;
                }
                level++;
            }
            return true;
        }
        //URL编码，避免URL中资源路径与查询字符串中的特殊字符与HTTP请求中特殊字符产生歧义
        //编码格式：将特殊字符的ascii值，转换为两个16进制字符，前缀%   C++ -> C%2B%2B
        //  不编码的特殊字符： RFC3986文档规定 . - _ ~ 字母，数字属于绝对不编码字符
        //RFC3986文档规定，编码格式 %HH 
        //W3C标准中规定，查询字符串中的空格，需要编码为+， 解码则是+转空格
        /// @brief 对Url进行解码 
        /// 遇到了%，则将紧随其后的两个字符转换为数字(一个字符对应的ascii值)，第一个数字左移四位，然后加上第二个数字
        static char HEXTOI(char c)
        {
            if(isdigit(c)){return c - '0';}
            else if(isupper(c)){return c - 'A' + 10;}
            else if(islower(c)){return c - 'a' + 10;}
            return -1;
        }
        /// 进行解码
        static std::string UrlDecode(std::string str,bool AddInToSep)
        {
            std::string res;
            for(int i = 0;i<str.size();i++)
            {
                if(str[i] == '%' && i<str.size()-2)
                {
                    char num = HEXTOI(str[i + 1]) *16 + HEXTOI(str[i + 2]);
                    res += num;
                    i+=2;
                }
                if(str[i] == '+' && AddInToSep == true){res += ' ';}
                res += str[i];
            }
            return res;
        }
        /// @brief 对Url进行编码  查询字符串中的sep要编码为+ 资源字符串中的sep不要
        static std::string UrlEncode(std::string str,bool SepInToAdd)
        {
            std::string res;
            for(auto ch : str)
            {
                ///如果是这四个特殊字符不编码,不是数字和字母的特殊字符全部编码
                //isalpha checks for an alphabetic character; in the standard "C" locale, it is equivalent to (isupper(c) || islower(c)).
                //isdigit checks for a digit (0~9)
                //isalnum = isalpha + isdigit
                if(ch == '.'||ch == '-'||ch == '_'||ch == '~'||isalnum(ch)){ res+=ch;continue;}
                if(SepInToAdd && ch == ' ')
                {
                    res += '+';
                }
                char buffer[4] = {0};
                //%% → 输出一个字面量 % ;剩下 %02X → 正常格式符，把 ch 打印成 2 位十六进制，大写，不足补 0
                snprintf(buffer,4,"%%%02X",ch);
                res+=buffer;
            }
            return res;
        }
        
    };
    //模块意义：对用户的请求进行存储，方便后续使用请求进行分析 GET /bitejiuyeke/login?user=xiaoming&pass=123123 HTTP/1.1
    /// 这里为什么要初始化一部分HttpRequest的成员变量，是因为在OnMessageCallback处理的时候，有可能上下文模块处理请求处理错误了，直接将
    /// 错误网页进行发送，此时可能会用到HttpRequest的一部分初始化成员变量，所以我们要提前设置好
    class HttpRequest
    {
    public:
        std::string _method;
        std::string _version; //版本
        std::string _path;    //资源路径  接收完成之后在判断是动态资源还是静态资源
        std::unordered_map<std::string, std::string> _params;           //查询字符串  key=value&key=value

        std::unordered_map<std::string, std::string> _headerfields;     //头部字段
        std::string _body;    //响应正文

        std::string _basedir; //静态资源根目录
        std::smatch _matches; //资源路径的正则提取数据    目的是为了让/search/1234这类的资源路径字段能够匹配 /search/(\\d.*)
    public:
        HttpRequest():_version("HTTP/1.1"){}
        void SetBasedir(std::string path)
        {
            if(!Util::IsDir(path))
            {
                DBG_LOG("it == _headerfields.end()");
                return;
            }
        }
        void SetMethod(std::string method){_method = method;}
        void SetVersion(std::string version){_version = version;}
        void SetPath(std::string path){_path = path;}
        void SetBody(std::string body){_body = body;}
        void ReSet() {
            _method.clear();
            _path.clear();
            _version = "HTTP/1.1";
            _body.clear();
            std::smatch match;
            _matches.swap(match);
            _headerfields.clear();
            _params.clear();
        }
        std::string GetHeader(std::string key)const
        {
            auto it = _headerfields.find(key);
            if(it == _headerfields.end())
            {
                //DBG_LOG("it == _headerfields.end()");
                return " ";
            }
            return it->second;
        }
        bool SetHeader(std::string key,std::string val)
        {
            auto it = _headerfields.find(key);
            if(it!=_headerfields.end())
            {
                DBG_LOG("this header exists");
                return false;
            }
            _headerfields.insert(std::make_pair(key,val));
            return true;
        }
        //_params; 设置查询字符串中key的值  key=value&key=value
        bool SetParmsVal(std::string key,std::string val)
        {
            auto it = _params.find(key);
            if(it!=_params.end())
            {
                DBG_LOG("this param exists");
                return false;
            }
            _params.insert(std::make_pair(key,val));
            return true;
        }
        //_params; 获得查询字符串中key的值  key=value&key=value
        std::string GetParmsVal(std::string key)
        {
            auto it = _params.find(key);
            if(it == _params.end())
            {
                DBG_LOG("it == _params.end()");
                return " ";
            }
            return it->second;
        }
        // 获得正文长度
        size_t GetLength()
        {
            std::string length = GetHeader("Content-Length");
            if(length == " ")return 0;
            size_t len = std::stoi(length);
            return len;
        }
        //判断是否是短链接
        bool Close()const
        {
            std::string val = GetHeader("Connection");
            if(val != " "||val == "keep-alive"){return false;}
            else return true;
        }
    };

    //模块意义：对回复格式进行组织，方便使用者直接进行内容填入，填入完成后直接进行发送
    class HttpResponse
    {
    public:
        std::string _version;
        int _status;
        std::unordered_map<std::string, std::string> _headers;  //头部字段
        std::string _body;              //响应正文
        bool _redirection_flag;         //是否开启重定向
        std::string _redirection_url;   //重定向目标url

        HttpResponse():_version("HTTP/1.1"),_status(200)
        {}
        void Reset()
        {
            _version = "HTTP/1.1";
            _status = 200;
            _headers.clear();
            _body = "";
            _redirection_flag = false;
            _redirection_url = "";
        }
        void SetVersion(std::string version){_version = version;}
        /// 在setcontent的过程中，将body设置进去，并且将对应的body的类型写入回复头中
        void SetContent(const std::string &body,  const std::string &type = "text/html") 
        {
            _body = body;
            Setheader("Content-Type", type);
        }
        bool Setheader(std::string key,std::string val)
        {
            auto it = _headers.find(key);
            if(it!=_headers.end())
            {
                //DBG_LOG("this header exists");
                return false;
            }
            _headers.insert(std::make_pair(key,val));
            return true;
        }
        std::string GetHeader(std::string key)
        {
            auto it = _headers.find(key);
            if(it == _headers.end())
            {
                DBG_LOG("it == _headers.end()");
                return " ";
            }
            return it->second;
        }
        void EnableRedirection(const std::string &url,int status = 302)
        {
            _redirection_flag = true;
            _redirection_url = url;
            _status = status;
        }

        bool Close()
        {
            std::string val = GetHeader("Connection");
            if(val != " "||val == "keep-alive"){return false;}
            else return true;
        }
    };
    #define MAX_LINE 8192
    /// @brief 这是一个上下文模块  
    typedef enum 
    {
        RECV_HTTP_ERROR, //接收请求出错
        RECV_HTTP_LINE,  //接收请求行中
        RECV_HTTP_HEAD,  //接收请求头中
        RECV_HTTP_BODY,  //接收正文中
        RECV_HTTP_OVER   //当前请求接收完毕
    }HttpRecvStatu;
    class HttpContext
    {
    public:
        int _resp_statu; //响应状态码  后续回复的状态码也需要上下文来帮忙管理
        HttpRecvStatu _status;       //代表当前上下文模块接收当前请求的进度
        HttpRequest   _http_request; //当前上下文内部保存的请求模块
        HttpContext():_status(RECV_HTTP_LINE),_resp_statu(200)
        {}
        void Reset(){
            _resp_statu = 200;
            _status = RECV_HTTP_LINE;
            _http_request.ReSet();
        }
        bool ParseLine(std::string line)
        {
            //DBG_LOG("bool ParseLine(std::string line)");
            std::smatch matches;
            std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?", std::regex::icase);
            /// 四个括号分别是，请求方法，资源路径，查询字符串，请求版本
            /// 因为要匹配上述字符串，只有在读取完整整行才会匹配，所以根据ret返回值来判断
            bool ret = std::regex_match(line, matches ,e);
            if(ret == false)
            {
                _status = RECV_HTTP_ERROR;
                _resp_statu = 400;
                return false;
            }
            /// 此时代表matches内部有所有需要的请求头字段
            _http_request.SetMethod(matches[1].str());
            DBG_LOG("_http_request.SetMethod: %s",_http_request._method.c_str());
            
            /// 请求中收到的小写get转换成大写的GET
            std::transform(_http_request._method.begin(), _http_request._method.end(), _http_request._method.begin(), ::toupper);
            //资源路径的获取，需要进行URL解码操作，但是不需要+转空格
            _http_request._path = Util::UrlDecode(matches[2], false);
            DBG_LOG("_http_request._path:%s",_http_request._path.c_str());
            std::vector<std::string> vec_quary;
            Util::Split(matches[3],"&",vec_quary);
            DBG_LOG("vec_quary: %ld",vec_quary.size());
            for(auto quary:vec_quary)
            {
                int pos = quary.find('=');
                if(pos == std::string::npos)
                {
                    _status = RECV_HTTP_ERROR;  ///如果找不到'='，说明请求出错，在server处理时进行判断，如果_status = RECV_HTTP_ERROR直接返回
                    _resp_statu = 400;
                    return false;
                }
                /// substr,传递的是起始位置和长度
                std::string key = Util::UrlDecode(quary.substr(0, pos), true);  
                std::string val = Util::UrlDecode(quary.substr(pos + 1), true);
                std::cout<<key<<":";
                std::cout<<val<<std::endl;
                _http_request.SetParmsVal(key,val);
            }
            /// 处理完查询字符串之后进行打印
            for(auto parm:_http_request._params)
            {
                std::string key = parm.first;
                std::string val = parm.second;
                std::cout<<key<<":";
                std::cout<<val<<std::endl;
            }
            _http_request.SetVersion(matches[4]);
            return true;
        }
        bool ReceiveLine(muduo::Buffer* buffer)
        {
            DBG_LOG("bool ReceiveLine(muduo::Buffer* buffer)");
            if(_status!=RECV_HTTP_LINE)
            {
                ERR_LOG("_status!=RECV_HTTP_LINE");
                return false;
            }
            /// 获得当前的一行内容
            std::string line = buffer->GetLine();
            //2. 需要考虑的一些要素：缓冲区中的数据不足一行， 获取的一行数据超大
            if (line.size() == 0) 
            {
                //缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
                if (buffer->ReadAbleSize() > MAX_LINE) 
                {
                    _status = RECV_HTTP_ERROR;
                    _resp_statu = 414;//URI TOO LONG
                    return false;
                }
                //缓冲区中数据不足一行，但是也不多，就等等新数据的到来
                return true;
            }
            bool ret = ParseLine(line);
            if(ret == true)
            {
                /// 如果成功，将状态进行更改，缓冲区位置向后移动
                _status = RECV_HTTP_HEAD;
                buffer->MoveReadOffset(line.size());
                return true;
            }
            else return false;
        }
        /// @brief 这个接口，接收一行请求头，将请求头保存在HttpRequest中 
        bool ParseHead(std::string line)
        {
            //DBG_LOG("bool ParseHead(std::string line)");
            if (line.back() == '\n') line.pop_back();//末尾是换行则去掉换行字符
            if (line.back() == '\r') line.pop_back();//末尾是回车则去掉回车字符
            int pos = line.find(":");
            if(pos == std::string::npos)
            {
                _status = RECV_HTTP_ERROR;
                _resp_statu = 400;
                return false;
            }
            std::string key = line.substr(0,pos);
            std::string val = line.substr(pos + 1);
            _http_request.SetHeader(key,val);
            return true;
        }
        /// 解析错误都直接返回false，最终遍历到\n 或者\r\n才终止循环
        bool ReceiveHead(muduo::Buffer* buffer)
        {
            DBG_LOG("bool ReceiveHead(muduo::Buffer* buffer)");
            if(_status!=RECV_HTTP_HEAD)
            {
                ERR_LOG("_status!=RECV_HTTP_HEAD");
                return false;
            }
            while(1)
            {
                /// 获得当前的一行内容
                std::string line = buffer->GetLine();
                //2. 需要考虑的一些要素：缓冲区中的数据不足一行， 获取的一行数据超大
                if (line.size() == 0) 
                {
                    //缓冲区中的数据不足一行，则需要判断缓冲区的可读数据长度，如果很长了都不足一行，这是有问题的
                    if (buffer->ReadAbleSize() > MAX_LINE) 
                    {
                        _status = RECV_HTTP_ERROR;
                        _resp_statu = 414;//URI TOO LONG
                        return false;
                    }
                    //缓冲区中数据不足一行，但是也不多，就等等新数据的到来
                    return true;
                }
                if(line == "\n" || line == "\r\n")
                {
                    /// 此时遇到了空行，将空行也给移除
                    buffer->MoveReadOffset(line.size());
                    break;
                }
                bool ret = ParseHead(line);
                if(ret == true)
                {
                    buffer->MoveReadOffset(line.size());
                }
                else{return false;}
            }
            _status = RECV_HTTP_BODY;
            return true;
        }
        /// 接收一个请求正文
        bool ReceiveBody(muduo::Buffer* buffer)
        {
            //DBG_LOG("bool ReceiveBody(muduo::Buffer* buffer)");
            if(_status!=RECV_HTTP_BODY)
            {
                ERR_LOG("_status!=RECV_HTTP_BODY");
                return false;
            }
            /// 获得正文部分的长度,头部字段中设置过了的     
            int content_length = _http_request.GetLength();
            int remaining_body = _http_request.GetLength() - _http_request._body.size();
            int readlen = buffer->ReadAbleSize();
            /// 如果当前缓冲区可读内容大于正文部分的长度，直接读取，注意读取的长度是剩余需要的长度(一开始是100，是更新的长度)
            if(readlen>=remaining_body)
            {
                _http_request._body = buffer->ReadAsStringAndPop(remaining_body);
                _status = RECV_HTTP_OVER;
                return true;
            }
            /// 当前缓冲区中内容不够,将缓冲区内容全部读取出来
            _http_request._body = buffer->ReadAsStringAndPop(readlen);
            return true;
        }

        HttpRequest GetRequest(){return _http_request;}
        void ReceiveRequest(muduo::Buffer* buffer)
        {
            DBG_LOG("ReceiveRequest(muduo::Buffer* buffer)");
            switch(_status) {
                case RECV_HTTP_LINE:
                ReceiveLine(buffer);
                case RECV_HTTP_HEAD:
                ReceiveHead(buffer);
                case RECV_HTTP_BODY:
                ReceiveBody(buffer);
            }
            /// 因为在每个调用函数中都有_status的判断，所以不用加停止条件，等到退出switch循环，自动就是最终处理的结果
            return;
        }   
    };

    // HttpContext* context = conn->getcontext()->get<HttpContext>();
    /// 这是一个HttpServer的代码，负责接收请求，获得完整请求后进行处理，(判断是静态资源处理，还是动态资源处理)
    /// 请求静态资源一般都是Get，Put两种方法，如果不是这两种方法肯定不是静态资源请求
    /// 在这两种方法之后，判断当前文件路径是否合理，判断文件是否存在来判断是否是静态资源请求，如果不是，那就是动态资源请求
    class HttpServer
    {
    using Functor = std::function<void(HttpRequest&,HttpResponse*)>;
    using Functor_task = std::function<void()>;
    using Method_Handlers = std::vector<std::pair<std::regex, Functor>>;
    using Get_Handlers = std::vector<std::pair<std::regex, Functor>>;
    using Post_Handlers = std::vector<std::pair<std::regex, Functor>>;
    using Put_Handlers = std::vector<std::pair<std::regex, Functor>>;
    using Delete_Handlers = std::vector<std::pair<std::regex, Functor>>;
    private:
        muduo::TcpServer _tcpserver;
        Get_Handlers _get_handlers;
        Post_Handlers _post_handlers;
        Put_Handlers _put_handlers;
        Delete_Handlers _delete_handlers;
    private:
        //构建一个错误内容进行发送
        void ErrorHandler(const HttpRequest& req,HttpResponse* rsp)
        {
            int status = rsp->_status;
            std::string s = "/" + std::to_string(status)+".html";
            /// path作为资源的路径存在
            std::string path = indexurl; 
            path.append(s);
            /// 将内容全部放入_body中
            bool ret = Util::ObtainTheFileContent(path,&(rsp->_body));
            if(ret == false)
            {
                /// 此时统一将400的错误写入body中
                Util::ObtainTheFileContent(std::string("wwwroot/400.html"),&(rsp->_body));
            }
            /// 此时body的内容已经设置进来了，只需要再设置一下请求头中的类型就行了
            rsp->Setheader("Content-Type","text/html");
        }
        //将请求构建进行发送 //std::string _version;
                           //int _status;
                           //std::unordered_map<std::string, std::string> _headers;  //头部字段
                           //std::string _body;              //响应正文
                           //bool _redirection_flag;         //是否开启重定向
                           //std::string _redirection_url;   //重定向目标url
        void WriteReponse(const muduo::PtrConnection& conn, const HttpRequest& req,HttpResponse*rsp)
        {
            // 判断是否是短链接"Connection"-"keep-alive"
            if(!req.Close())
            {
                rsp->Setheader("Connection","keep-alive");
            }
            else rsp->Setheader("Connection","close");
            // 如果开启了重定向的话，就设置重定向
            if(rsp->_redirection_flag == true){rsp->Setheader("Location",rsp->_redirection_url);}
            int rsp_content_length = rsp->_body.size();
            std::cout<<rsp_content_length<<std::endl;
            rsp->Setheader("Content-Length",std::to_string(rsp_content_length));
            //别加任何 charset，别用 text/*，把 body 当字节原样发。例如发送一张图片
            rsp->Setheader("Content-Type","application/octet-stream"); 
            //std::stringstream（头文件 <sstream>）是 C++ 的“内存字符串流”，
            //用法和 cin/cout 几乎一样，只是目标/源是内存里的 std::string，常用来做类型转换或拼串。
            std::stringstream ss;
            ss << rsp->_version << " " << std::to_string(rsp->_status) << " " << Util::IntToDesc(rsp->_status) << "\r\n";
            for(auto parm:rsp->_headers)
            {
                std::string key = parm.first;
                std::string val = parm.second;
                ss << key << ":" << val << "\r\n";
            }
            ss << "\r\n";
            ss << rsp->_body;
            std::string response = ss.str();
            conn->Send(response.c_str(),response.size());
            return;
        }
        //进行静态资源的处理
        void FileHandler(const HttpRequest& req,HttpResponse* rsp)
        {
            DBG_LOG("void FileHandler(const HttpRequest& req,HttpResponse* rsp)");
            std::string path = req._path;
            if(path == "/")
            {
                path = indexurl + path + indexhtml;
                /*path += indexurl;
                path += "/";
                path += indexhtml;*/
            }
            else path = indexurl + path;  //代表 "wwwroot"
            //判断路径是否合理，不合理直接调用返回错误页面，直接发送
            bool res = Util::ValidPath(path);
            if(res == false)
            {
                rsp->_status = 404; //代表NOT FOUND 请求资源无法找到
                return ErrorHandler(req,rsp);
            }
            /// 此时代表真正处理静态资源
            bool ret = Util::ObtainTheFileContent(path,&(rsp->_body));
            if(ret == false)
            {
                /// 此时统一将400的错误写入body中
                Util::ObtainTheFileContent(std::string("wwwroot/400.html"),&(rsp->_body));
            }
            /// 此时body的内容已经设置进来了，只需要再设置一下请求头中的类型就行了
            std::cout<<rsp->_body<<std::endl;
            std::string mime = Util::ExtMime(path);
            rsp->Setheader("Content-Type",mime);
        }
        //对请求进行分辨，是否是一个静态资源请求
        bool IsStaticResources(const HttpRequest& req)
        {
            DBG_LOG("bool IsStaticResources(const HttpRequest& req)");
            /// 1.判断一个请求的资源路径是否是一个静态资源请求时，先去判断请求的请求方法
            ///GET/HEAD → 静态资源  PUT/POST/DELETE/PATCH → 动态接口或上传
            if(req._method != "GET" && req._method != "HEAD")return false;
            /// 2.判断请求路径是否是目录 ,因为要保证资源路径的干净，如果不是静态资源后续要访问动态资源，所以创建一个临时对象用来进行判断
            std::string path = req._path;
            DBG_LOG("path:%s",path.c_str());
            if(Util::IsDir(path))
            {
                //注意此时有一种特殊情况就是访问的是根目录 "/"  因为http协议在发送请求的时候会将请求的路径以/为开始，除非html点击了表单
                if(path== "/")
                {
                    DBG_LOG("path== /");
                    /*path += indexurl;
                    path += "/";
                    path += indexhtml;*/
                    path = indexurl + path + indexhtml;
                    return true;
                }
                return false;
            }
            /// 3.此时代表访问的路径不是一个目录，判断是否是一个文件
            if(Util::IsRegular(path))
            {
                return true;
            }
            return false;
        }
        //对一个功能性请求进行处理,需要先进行判断
        void Dispatcher(HttpRequest& req,HttpResponse* rsp,Method_Handlers handlers)
        {
            for(auto handler : handlers)
            {
                /// 先获得正则表达式匹配元
                std::regex r= handler.first;
                bool ret = std::regex_match(req._path,req._matches,r);
                if(ret == true)
                {
                    Functor functor = handler.second;
                    functor(req,rsp);
                    return;
                }
            }
            rsp->_status = 404;  //表示NOT FOUND
            ErrorHandler(req,rsp);
            return;
        }
        void Route(HttpRequest& req,HttpResponse* rsp)
        {
            DBG_LOG(" void Route(HttpRequest& req,HttpResponse* rsp)");
            //1. 对请求进行分辨，是一个静态资源请求，还是一个功能性请求
            //   静态资源请求，则进行静态资源的处理
            //   功能性请求，则需要通过几个请求路由表来确定是否有处理函数
            //   既不是静态资源请求，也没有设置对应的功能性请求处理函数，就返回405
            if (IsStaticResources(req) == true) 
            {
                //是一个静态资源请求, 则进行静态资源请求的处理
                return FileHandler(req, rsp);
            }
            if (req._method == "GET" || req._method == "HEAD") 
            {
                return Dispatcher(req, rsp,  _get_handlers);
            }
            else if (req._method == "POST") 
            {
                return Dispatcher(req, rsp,  _post_handlers);
            }
            else if (req._method == "PUT") 
            {
                return Dispatcher(req, rsp, _put_handlers);
            }
            else if (req._method == "DELETE") 
            {
                return Dispatcher(req, rsp, _delete_handlers);
            }
            rsp->_status = 405;// Method Not Allowed
            ErrorHandler(req,rsp);  ///统一也都返回一个错误页面
            return ;
        }
        /// @brief 接收消息之后调用的处理函数
        void OnMessageCallback(const muduo::PtrConnection& conn, muduo::Buffer *buffer)
        {
            while(buffer->ReadAbleSize() > 0){
                if(conn == nullptr){
                    ERR_LOG("conn == nullptr");
                    return;
                }
                /// 先获得当前连接保存的Any context，Any内部的指针指向一个真正的上下文处理模块，即HttpContext对象
                /// 这里在处理时，获取HttpContext对象的指针来进行操作
                HttpContext* context = conn->GetContext()->get<HttpContext>();
                HttpRequest req;
                HttpResponse rsp;
                context->ReceiveRequest(buffer);
                /// 获得处理后的req
                req = context->GetRequest();
                rsp._status = context->_resp_statu;
                if (context->_resp_statu >= 400) {
                    //进行错误响应，关闭连接
                    ErrorHandler(req, &rsp);//填充一个错误显示页面数据到rsp中
                    WriteReponse(conn, req, &rsp);//组织响应发送给客户端
                    context->Reset();
                    /// 这里为什么要清空呢？就是怕在处理当前连接数据的时候出错了，出错了此时并不会去立即
                    /// 关闭连接，而是压入栈中,等到这次循环结束，最后再执行
                    /// 因为服务器可能读取的是多个http请求，如果这个请求前面的请求正确处理了就会开启写事件就绪
                    /// 此时在写事件中就会判断inbuffer中还有数据继续执行OnMessageCallback(你不能保证inbuffer中)
                    /// 而此时就会因为inbuffer中的内容无法解析成功请求头
                    buffer->MoveReadOffset(buffer->ReadAbleSize());//出错了就把缓冲区数据清空
                    conn->Shutdown();//关闭连接
                    return;
                }
                if(context->_status != RECV_HTTP_OVER){return;}
                /// 此时表面当前请求是一个完整的请求了
                //DBG_LOG("得到完整请求了");
                Route(req,&rsp);
                /// 会先将SendInLoop全部压入任务池中conn->Send(response.c_str(),response.size())，开启写事件就绪，将数据一起发送
                /// 完成将一个连接一次发送的多个请求一起返回这个功能
                WriteReponse(conn, req, &rsp);
                context->Reset();
                //6. 根据长短连接判断是否关闭连接或者继续处理
                if (rsp.Close() == true) conn->Shutdown();//短链接则直接关闭
            }
        }
        void OnConnectedCallback(const muduo::PtrConnection& conn)
        {
            /// 很重要，初始化一个连接的上下文
            conn->SetContext(HttpContext());
            DBG_LOG("NEW CONNECTION %p", conn.get());
        }
        void OnClosedCallback(const muduo::PtrConnection& conn)
        {
            DBG_LOG("CLOSE CONNECTION %p", conn.get());
        }
    public:
        HttpServer(int port,bool EnableInactiveRelease = false,int delay = DEFAULT_TIMEOUT):_tcpserver(port)
        {
            if(EnableInactiveRelease == true){_tcpserver.EnableInactiveRelease(delay);}
            _tcpserver.SetMessageCallback(std::bind(&HttpServer::OnMessageCallback,this,std::placeholders::_1,std::placeholders::_2));
            _tcpserver.SetConnectedCallback(std::bind(&HttpServer::OnConnectedCallback,this,std::placeholders::_1));
            _tcpserver.SetClosedCallback(std::bind(&HttpServer::OnClosedCallback,this,std::placeholders::_1));
        }
        void SetThreadCount(int count){_tcpserver.SetThreadCount(count);}
        void Start(){_tcpserver.Start();}
        void RunAfter(const Functor_task &task, int delay) {
            _tcpserver.RunAfter(task,delay);
        }
        /*设置/添加，请求（请求的正则表达）与处理函数的映射关系*/
        void Get(const std::string &pattern, const Functor &handler) {
            _get_handlers.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Post(const std::string &pattern, const Functor &handler) {
            _post_handlers.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Put(const std::string &pattern, const Functor &handler) {
            _put_handlers.push_back(std::make_pair(std::regex(pattern), handler));
        }
        void Delete(const std::string &pattern, const Functor &handler) {
            _delete_handlers.push_back(std::make_pair(std::regex(pattern), handler));
        }
    };
};
/////// 最终调试：简单来说逻辑，分模块都没有问题，问题出现在最后调用的时候在解析OnMessageCallback中出问题，解码和编码相互实现出错
/////// 以及setcontent是用来设置Setheader("Content-Type", type); 从而导致Content-Length插入错误，导致最终一系列的问题
